using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[Serializable]
[RequireComponent(typeof(ScrollRect))]
public class RecycleVerticalListView : MonoBehaviour
{
	public class ViwedCellHelper
	{
		public GameObject cell;

		public int index;

		public ViwedCellHelper(GameObject cell, int index)
		{
			this.cell = cell;
			this.index = index;
		}
	}

	[Header("Default settings")]
	public RectTransform ContentRecTransform;

	[Header("Scroll settings")]
	public bool AlwaysScroll = true;

	[Header("Delegate")]
	[InterfaceOutlet(typeof(IRecycleVerticalListViewDelegate))]
	public UnityEngine.Object component;

	public IRecycleVerticalListViewDelegate listViewDelegate;

	private int ListLength;

	private ScrollRect scrollRect;

	private GameObject CellPrefab;

	private float DistanceBetweenElements = 10f;

	private float numberOfCellsInViewPort;

	private float totalNumberOfCells;

	private float cellPrefabHeigth;

	private float scrollRectNeededHeigth;

	[Header("Refresh settings - not required")]
	public bool userefresh;

	public bool RotateRefreshImage;

	public float TopOffsetForRefreshing = 50f;

	public Image refreshImage;

	public UnityEvent OnRefresh;

	[HideInInspector]
	public bool refreshLock;

	private Queue<GameObject> unusedCells = new Queue<GameObject>();

	private List<ViwedCellHelper> usedCells = new List<ViwedCellHelper>();

	private void Awake()
	{
		if (component != null)
		{
			listViewDelegate = (IRecycleVerticalListViewDelegate)component;
		}
		else
		{
			UnityEngine.Debug.LogError("VerticalListView must have a delegate to work!");
		}
		scrollRect = GetComponent<ScrollRect>();
	}

	private void Start()
	{
		StartCoroutine(InitializeList(null));
	}

	public void UpdateList(Action onFinished)
	{
		StartCoroutine(InitializeList(onFinished));
	}

	private IEnumerator InitializeList(Action onFinished)
	{
		yield return new WaitForEndOfFrame();
		foreach (ViwedCellHelper usedCell in usedCells)
		{
			UnityEngine.Object.Destroy(usedCell.cell);
		}
		usedCells.Clear();
		foreach (GameObject unusedCell in unusedCells)
		{
			UnityEngine.Object.Destroy(unusedCell);
		}
		unusedCells.Clear();
		ContentRecTransform.gameObject.SetActive(value: true);
		if (listViewDelegate != null)
		{
			ListLength = listViewDelegate.GetNumOfCells();
			if (ListLength < 0)
			{
				UnityEngine.Debug.LogError("List length can not be less than 0!");
				yield break;
			}
		}
		if (listViewDelegate != null)
		{
			CellPrefab = listViewDelegate.GetCellPrefab();
			ValidateCellPrefab(CellPrefab);
		}
		if (listViewDelegate != null)
		{
			DistanceBetweenElements = listViewDelegate.GetCellDistance();
		}
		scrollRectNeededHeigth = (float)ListLength * (DistanceBetweenElements + cellPrefabHeigth) - DistanceBetweenElements;
		CreateList();
		refreshLock = true;
		yield return null;
		onFinished.Fire();
	}

	private void ValidateCellPrefab(GameObject cellPrefab)
	{
		RectTransform rectTransform = cellPrefab.transform as RectTransform;
		if (rectTransform.anchorMax != new Vector2(1f, 1f) || rectTransform.anchorMin != new Vector2(0f, 1f))
		{
			UnityEngine.Debug.LogError("CellPrefab Anchors must be Stretch Top!");
		}
		GetCellHeightFromPrefab();
	}

	private float GetScrollNormalizedPosition()
	{
		return Mathf.Clamp01(scrollRect.verticalNormalizedPosition);
	}

	private float GetViewPortNormalizedSize()
	{
		return (base.transform as RectTransform).rect.height / scrollRectNeededHeigth;
	}

	private RangeInt GetIndexRangeOfViwableCells()
	{
		float height = (base.transform as RectTransform).rect.height;
		float num = cellPrefabHeigth + DistanceBetweenElements;
		Vector2 offsetMax = ContentRecTransform.offsetMax;
		float y = offsetMax.y;
		int num2 = (int)Mathf.Floor(y / num);
		if (num2 < 0)
		{
			num2 = 0;
		}
		int num3 = (int)Mathf.Ceil(height / num);
		if (num2 + num3 >= ListLength)
		{
			num3 = ListLength - num2;
		}
		return new RangeInt(num2, num3);
	}

	private void Update()
	{
		if (!userefresh)
		{
			return;
		}
		if (!refreshLock)
		{
			Vector2 offsetMax = ContentRecTransform.offsetMax;
			if (offsetMax.y < 0f)
			{
				float num = ContentRecTransform.offsetMax.magnitude / TopOffsetForRefreshing;
				if (ContentRecTransform.offsetMax.magnitude < TopOffsetForRefreshing)
				{
					Color color = refreshImage.color;
					color.a = num;
					refreshImage.color = color;
					if (RotateRefreshImage)
					{
						refreshImage.transform.rotation = new Quaternion(0f, 0f, 0f, 0f);
						refreshImage.transform.Rotate(0f, 0f, -360f * num);
					}
				}
				else
				{
					Color color2 = refreshImage.color;
					color2.a = 1f;
					refreshImage.color = color2;
					refreshLock = true;
					OnRefresh.Invoke();
				}
				return;
			}
		}
		if (ContentRecTransform.offsetMax.magnitude == 0f)
		{
			refreshLock = false;
		}
	}

	private void GetCellHeightFromPrefab()
	{
		Vector2 offsetMin = (CellPrefab.transform as RectTransform).offsetMin;
		float num = 0f - offsetMin.y;
		Vector2 offsetMax = (CellPrefab.transform as RectTransform).offsetMax;
		cellPrefabHeigth = num + offsetMax.y;
	}

	private void CreateList()
	{
		if (!CellPrefab)
		{
			UnityEngine.Debug.LogWarning("No Cell Prefab set.");
			return;
		}
		numberOfCellsInViewPort = Mathf.Ceil((base.transform as RectTransform).rect.height / (DistanceBetweenElements + cellPrefabHeigth));
		if (numberOfCellsInViewPort > (float)ListLength)
		{
			numberOfCellsInViewPort = ListLength;
		}
		totalNumberOfCells = numberOfCellsInViewPort + 1f;
		for (int i = 0; (float)i < totalNumberOfCells; i++)
		{
			GameObject gameObject = UnityEngine.Object.Instantiate(CellPrefab);
			gameObject.transform.SetParent(ContentRecTransform, worldPositionStays: false);
			gameObject.name = CellPrefab.name + "[unused]";
			gameObject.SetActive(value: false);
			unusedCells.Enqueue(gameObject);
		}
		SetContentRectHeight(scrollRectNeededHeigth);
		for (int j = 0; (float)j < numberOfCellsInViewPort && j < ListLength; j++)
		{
			GameObject reusabelCell = GetReusabelCell();
			usedCells.Add(new ViwedCellHelper(reusabelCell, j));
			reusabelCell.name = CellPrefab.name;
			if (listViewDelegate != null)
			{
				listViewDelegate.PopulateEachCell(reusabelCell.gameObject, j);
			}
			SetListItemRectPosByIndex(reusabelCell.transform as RectTransform, j);
		}
	}

	private GameObject GetReusabelCell()
	{
		GameObject gameObject = unusedCells.Dequeue();
		gameObject.SetActive(value: true);
		return gameObject;
	}

	private void TestForRectAnchorPositions(RectTransform rt)
	{
		Vector2 anchorMin = rt.anchorMin;
		if (anchorMin.x == 0f)
		{
			Vector2 anchorMin2 = rt.anchorMin;
			if (anchorMin2.y == 1f)
			{
				Vector2 anchorMax = rt.anchorMax;
				if (anchorMax.x == 1f)
				{
					Vector2 anchorMax2 = rt.anchorMax;
					if (anchorMax2.y == 1f)
					{
						return;
					}
				}
			}
		}
		throw new Exception("All ListItems inside an ListView must have anchor position as Stretch-Top!");
	}

	private void SetListItemRectPosByIndex(RectTransform listItem, int index)
	{
		float num = (cellPrefabHeigth + DistanceBetweenElements) * (float)index;
		listItem.anchorMin = new Vector2(0f, 1f);
		listItem.anchorMax = new Vector2(1f, 1f);
		listItem.offsetMax = -new Vector2(0f, num);
		listItem.offsetMin = -new Vector2(0f, num + cellPrefabHeigth);
	}

	private void SetContentRectHeight(float neededHeigth)
	{
		ContentRecTransform.anchorMax = new Vector2(1f, 1f);
		ContentRecTransform.anchorMin = new Vector2(0f, 1f);
		ContentRecTransform.offsetMax = new Vector2(0f, 0f);
		if (neededHeigth < (base.gameObject.transform as RectTransform).rect.height)
		{
			ContentRecTransform.offsetMin = new Vector2(0f, 0f - (base.gameObject.transform as RectTransform).rect.height);
		}
		else
		{
			ContentRecTransform.offsetMin = new Vector2(0f, 0f - neededHeigth);
		}
		ScrollRect scrollRect = base.gameObject.GetComponent<ScrollRect>();
		Vector2 sizeDelta = ContentRecTransform.sizeDelta;
		if (sizeDelta.y <= (scrollRect.transform as RectTransform).rect.height && !AlwaysScroll)
		{
			scrollRect.vertical = false;
		}
		scrollRect.verticalNormalizedPosition = 1f;
	}

	private bool IsCellInViewPort(int index)
	{
		return (from obj in usedCells
			where obj.index == index
			select obj).SingleOrDefault() != null;
	}

	public void UpdateViewableCells(Vector2 pos)
	{
		RangeInt indexRangeOfViwableCells = GetIndexRangeOfViwableCells();
		List<ViwedCellHelper> list = new List<ViwedCellHelper>();
		foreach (ViwedCellHelper usedCell in usedCells)
		{
			if (usedCell.index < indexRangeOfViwableCells.start || usedCell.index > indexRangeOfViwableCells.end)
			{
				list.Add(usedCell);
			}
		}
		foreach (ViwedCellHelper item in list)
		{
			usedCells.Remove(item);
			item.cell.SetActive(value: false);
			unusedCells.Enqueue(item.cell);
			item.cell = null;
		}
		for (int i = indexRangeOfViwableCells.start; i <= indexRangeOfViwableCells.end && i < ListLength; i++)
		{
			if (!IsCellInViewPort(i) && i >= 0)
			{
				GameObject reusabelCell = GetReusabelCell();
				usedCells.Add(new ViwedCellHelper(reusabelCell.gameObject, i));
				reusabelCell.name = CellPrefab.name;
				if (listViewDelegate != null)
				{
					listViewDelegate.PopulateEachCell(reusabelCell.gameObject, i);
				}
				SetListItemRectPosByIndex(reusabelCell.transform as RectTransform, i);
			}
		}
	}

	public void CenterInIndex(int index)
	{
		float y = (float)(index + 1) * (scrollRect.content.rect.height / (float)ListLength) - scrollRect.content.rect.height / 2f;
		scrollRect.content.localPosition = new Vector3(0f, y, 0f);
	}
}
